﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using DocumentFormat.OpenXml.Packaging;
using docTissV2.Enumeradores;
using System.IO;
using System.Xml.Serialization;


namespace docTissV2.Classes
{
    public static class XMLUtils
    {
        #region Constantes
        public const string NamespaceANS = "http://www.ans.gov.br/padroes/tiss/schemas";
        #endregion

        #region Métodos
        /// <summary>
        /// Gets the inner XML of the xDoc parameter, removing the optional nodes if they are empty
        /// </summary>
        /// <param name="xDoc">The XMLDocument that needs to be normalized</param>
        /// <param name="tipoGuia">The type of the Guia</param>
        /// <returns>A String with the XML syntax without optional nodes that were empty</returns>
        /// 
        public static string GetNormalizedXMLString(XmlDocument xDoc)
        {
            XmlNamespaceManager nsManager = new XmlNamespaceManager(xDoc.NameTable);
            nsManager.AddNamespace("ans", "http://www.ans.gov.br/padroes/tiss/schemas");

            XmlNodeList nodes = xDoc.LastChild.ChildNodes;

            for (int i = nodes.Count - 1; i >= 0; i--)
            {
                RemoveChildNodesIfEmpty(nodes[i]);
            }

            return xDoc.InnerXml;
        }

        private static void RemoveChildNodesIfEmpty(this XmlNode node)
        {
            if (node.HasChildNodes)
            {
                for (int i = node.ChildNodes.Count - 1; i >= 0; i--)
                    RemoveChildNodesIfEmpty(node.ChildNodes[i]);
                if (!node.HasChildNodes)
                    node.ParentNode.RemoveChild(node);
            }
            else if ((node.InnerXml == "" || node.InnerXml == null) && (node.Value == "" || node.Value == null))
                node.ParentNode.RemoveChild(node);
        }

        public static string BuscaXPathCodigoPrestadorExistente(XPathCustomXml xPathGuia, XmlDocument xDoc, XmlNamespaceManager nsManager)
        {
            if (xDoc.SelectSingleNode(xPathGuia.CPF, nsManager) != null)
                return xPathGuia.CPF;
            else if (xDoc.SelectSingleNode(xPathGuia.CNPJ, nsManager) != null)
                return xPathGuia.CNPJ;
            else if (xDoc.SelectSingleNode(xPathGuia.CodigoPrestadorNaOperadora, nsManager) != null)
                return xPathGuia.CodigoPrestadorNaOperadora;

            return null;
        }

        public static XmlNode RenameNode(XmlNode e, string newName)
        {
            XmlDocument doc = e.OwnerDocument;
            XmlNode newElement = doc.CreateNode(XmlNodeType.Element, "ans", newName, "http://www.ans.gov.br/padroes/tiss/schemas");
            while (e.HasChildNodes)
            {
                newElement.AppendChild(e.FirstChild);
            }
            XmlAttributeCollection ac = e.Attributes;
            while (ac.Count > 0)
            {
                newElement.Attributes.Append(ac[0]);
            }
            XmlNode parent = e.ParentNode;
            parent.ReplaceChild(newElement, e);
            return newElement;
        }

        public static XmlDocument SubstituiCustomControlXmlMapping(TipoGuia tipoGuia, XmlDocument xmlMainDoc, TipoCustomControlXmlMapping tipoCustomControlNovo, TipoProfissional tipoProfissional)
        {
            XmlNamespaceManager nsManager = new XmlNamespaceManager(xmlMainDoc.NameTable);
            nsManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");

            string xPathAntigo = "";
            string xPathNovo = "";

            #region xPath
            switch (tipoGuia)
            {
                case TipoGuia.Consulta:
                    if (xmlMainDoc.InnerXml.Contains("/ns0:guiaConsulta[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:CPF[1]"))
                        xPathAntigo = "/ns0:guiaConsulta[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:CPF[1]";
                    else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaConsulta[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:CNPJ[1]"))
                        xPathAntigo = "/ns0:guiaConsulta[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                    else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaConsulta[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]"))
                        xPathAntigo = "/ns0:guiaConsulta[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";

                    switch (tipoCustomControlNovo)
                    {
                        case TipoCustomControlXmlMapping.CPF:
                            xPathNovo = "/ns0:guiaConsulta[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:CPF[1]";
                            break;
                        case TipoCustomControlXmlMapping.CNPJ:
                            xPathNovo = "/ns0:guiaConsulta[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                            break;
                        case TipoCustomControlXmlMapping.CodigoPrestadorNaOperadora:
                            xPathNovo = "/ns0:guiaConsulta[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";
                            break;
                    }
                    break;
                case TipoGuia.SP_SADT:
                    switch (tipoProfissional)
                    {
                        case TipoProfissional.ProfissionalSolicitante:
                            if (xmlMainDoc.InnerXml.Contains("/ns0:guiaSP_SADT[1]/ns0:dadosSolicitante[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:CPF[1]"))
                                xPathAntigo = "/ns0:guiaSP_SADT[1]/ns0:dadosSolicitante[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:CPF[1]";
                            else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaSP_SADT[1]/ns0:dadosSolicitante[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:CNPJ[1]"))
                                xPathAntigo = "/ns0:guiaSP_SADT[1]/ns0:dadosSolicitante[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                            else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaSP_SADT[1]/ns0:dadosSolicitante[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]"))
                                xPathAntigo = "/ns0:guiaSP_SADT[1]/ns0:dadosSolicitante[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";

                            switch (tipoCustomControlNovo)
                            {
                                case TipoCustomControlXmlMapping.CPF:
                                    xPathNovo = "/ns0:guiaSP_SADT[1]/ns0:dadosSolicitante[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:CPF[1]";
                                    break;
                                case TipoCustomControlXmlMapping.CNPJ:
                                    xPathNovo = "/ns0:guiaSP_SADT[1]/ns0:dadosSolicitante[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                                    break;
                                case TipoCustomControlXmlMapping.CodigoPrestadorNaOperadora:
                                    xPathNovo = "/ns0:guiaSP_SADT[1]/ns0:dadosSolicitante[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";
                                    break;
                            }
                            break;
                        case TipoProfissional.ProfissionalExecutante:
                            if (xmlMainDoc.InnerXml.Contains("/ns0:guiaSP_SADT[1]/ns0:prestadorExecutante[1]/ns0:identificacao[1]/ns0:CPF[1]"))
                                xPathAntigo = "/ns0:guiaSP_SADT[1]/ns0:prestadorExecutante[1]/ns0:identificacao[1]/ns0:CPF[1]";
                            else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaSP_SADT[1]/ns0:prestadorExecutante[1]/ns0:identificacao[1]/ns0:CNPJ[1]"))
                                xPathAntigo = "/ns0:guiaSP_SADT[1]/ns0:prestadorExecutante[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                            else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaSP_SADT[1]/ns0:prestadorExecutante[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]"))
                                xPathAntigo = "/ns0:guiaSP_SADT[1]/ns0:prestadorExecutante[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";

                            switch (tipoCustomControlNovo)
                            {
                                case TipoCustomControlXmlMapping.CPF:
                                    xPathNovo = "/ns0:guiaSP_SADT[1]/ns0:prestadorExecutante[1]/ns0:identificacao[1]/ns0:CPF[1]";
                                    break;
                                case TipoCustomControlXmlMapping.CNPJ:
                                    xPathNovo = "/ns0:guiaSP_SADT[1]/ns0:prestadorExecutante[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                                    break;
                                case TipoCustomControlXmlMapping.CodigoPrestadorNaOperadora:
                                    xPathNovo = "/ns0:guiaSP_SADT[1]/ns0:prestadorExecutante[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";
                                    break;
                            }
                            break;
                        default:
                            throw new Exception();
                    }
                    break;
                case TipoGuia.Resumo_Internação:
                    if (xmlMainDoc.InnerXml.Contains("/ns0:guiaResumoInternacao[1]/ns0:identificacaoExecutante[1]/ns0:identificacao[1]/ns0:CPF[1]"))
                        xPathAntigo = "/ns0:guiaResumoInternacao[1]/ns0:identificacaoExecutante[1]/ns0:identificacao[1]/ns0:CPF[1]";
                    else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaResumoInternacao[1]/ns0:identificacaoExecutante[1]/ns0:identificacao[1]/ns0:CNPJ[1]"))
                        xPathAntigo = "/ns0:guiaResumoInternacao[1]/ns0:identificacaoExecutante[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                    else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaResumoInternacao[1]/ns0:identificacaoExecutante[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]"))
                        xPathAntigo = "/ns0:guiaResumoInternacao[1]/ns0:identificacaoExecutante[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";

                    switch (tipoCustomControlNovo)
                    {
                        case TipoCustomControlXmlMapping.CPF:
                            xPathNovo = "/ns0:guiaResumoInternacao[1]/ns0:identificacaoExecutante[1]/ns0:identificacao[1]/ns0:CPF[1]";
                            break;
                        case TipoCustomControlXmlMapping.CNPJ:
                            xPathNovo = "/ns0:guiaResumoInternacao[1]/ns0:identificacaoExecutante[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                            break;
                        case TipoCustomControlXmlMapping.CodigoPrestadorNaOperadora:
                            xPathNovo = "/ns0:guiaResumoInternacao[1]/ns0:identificacaoExecutante[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";
                            break;
                    }
                    break;
                case TipoGuia.Honorário_Individual:
                    switch (tipoProfissional)
                    {
                        case TipoProfissional.ProfissionalSolicitante:
                            if (xmlMainDoc.InnerXml.Contains("/ns0:guiaHonorarioIndividual[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:CPF[1]"))
                                xPathAntigo = "/ns0:guiaHonorarioIndividual[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:CPF[1]";
                            else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaHonorarioIndividual[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:CNPJ[1]"))
                                xPathAntigo = "/ns0:guiaHonorarioIndividual[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                            else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaHonorarioIndividual[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]"))
                                xPathAntigo = "/ns0:guiaHonorarioIndividual[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";

                            switch (tipoCustomControlNovo)
                            {
                                case TipoCustomControlXmlMapping.CPF:
                                    xPathNovo = "/ns0:guiaHonorarioIndividual[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:CPF[1]";
                                    break;
                                case TipoCustomControlXmlMapping.CNPJ:
                                    xPathNovo = "/ns0:guiaHonorarioIndividual[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                                    break;
                                case TipoCustomControlXmlMapping.CodigoPrestadorNaOperadora:
                                    xPathNovo = "/ns0:guiaHonorarioIndividual[1]/ns0:contratado[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";
                                    break;
                            }
                            break;
                        case TipoProfissional.ProfissionalExecutante:
                            if (xmlMainDoc.InnerXml.Contains("/ns0:guiaHonorarioIndividual[1]/ns0:contratadoExecutante[1]/ns0:identificacao[1]/ns0:CPF[1]"))
                                xPathAntigo = "/ns0:guiaHonorarioIndividual[1]/ns0:contratadoExecutante[1]/ns0:identificacao[1]/ns0:CPF[1]";
                            else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaHonorarioIndividual[1]/ns0:contratadoExecutante[1]/ns0:identificacao[1]/ns0:CNPJ[1]"))
                                xPathAntigo = "/ns0:guiaHonorarioIndividual[1]/ns0:contratadoExecutante[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                            else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaHonorarioIndividual[1]/ns0:contratadoExecutante[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]"))
                                xPathAntigo = "/ns0:guiaHonorarioIndividual[1]/ns0:contratadoExecutante[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";

                            switch (tipoCustomControlNovo)
                            {
                                case TipoCustomControlXmlMapping.CPF:
                                    xPathNovo = "/ns0:guiaHonorarioIndividual[1]/ns0:contratadoExecutante[1]/ns0:identificacao[1]/ns0:CPF[1]";
                                    break;
                                case TipoCustomControlXmlMapping.CNPJ:
                                    xPathNovo = "/ns0:guiaHonorarioIndividual[1]/ns0:contratadoExecutante[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                                    break;
                                case TipoCustomControlXmlMapping.CodigoPrestadorNaOperadora:
                                    xPathNovo = "/ns0:guiaHonorarioIndividual[1]/ns0:contratadoExecutante[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";
                                    break;
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                case TipoGuia.Odontologia:
                    if (xmlMainDoc.InnerXml.Contains("/ns0:guiaOdontologia[1]/ns0:dadosContratadoExecutante[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:CPF[1]"))
                        xPathAntigo = "/ns0:guiaOdontologia[1]/ns0:dadosContratadoExecutante[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:CPF[1]";
                    else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaOdontologia[1]/ns0:dadosContratadoExecutante[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:CNPJ[1]"))
                        xPathAntigo = "/ns0:guiaOdontologia[1]/ns0:dadosContratadoExecutante[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                    else if (xmlMainDoc.InnerXml.Contains("/ns0:guiaOdontologia[1]/ns0:dadosContratadoExecutante[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]"))
                        xPathAntigo = "/ns0:guiaOdontologia[1]/ns0:dadosContratadoExecutante[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";

                    switch (tipoCustomControlNovo)
                    {
                        case TipoCustomControlXmlMapping.CPF:
                            xPathNovo = "/ns0:guiaOdontologia[1]/ns0:dadosContratadoExecutante[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:CPF[1]";
                            break;
                        case TipoCustomControlXmlMapping.CNPJ:
                            xPathNovo = "/ns0:guiaOdontologia[1]/ns0:dadosContratadoExecutante[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:CNPJ[1]";
                            break;
                        case TipoCustomControlXmlMapping.CodigoPrestadorNaOperadora:
                            xPathNovo = "/ns0:guiaOdontologia[1]/ns0:dadosContratadoExecutante[1]/ns0:dadosContratado[1]/ns0:identificacao[1]/ns0:codigoPrestadorNaOperadora[1]";
                            break;
                    }
                    break;
                case TipoGuia.Solicitação_Internação:
                    break;
                case TipoGuia.Solicitação_Prorrogação:
                    break;
                case TipoGuia.Solicitação_SADT:
                    break;
                case TipoGuia.Solicitação_Odontologia:
                default:
                    break;
            }
            #endregion

            if ((!xPathAntigo.Equals("")) && (!xPathNovo.Equals("")))
            {
                xmlMainDoc.SelectSingleNode("//w:dataBinding[@w:xpath='" + xPathAntigo + "']",
                    nsManager).Attributes["w:xpath"].Value = xPathNovo;
            }

            return xmlMainDoc;
        }

        public static void SubstituiCustomXmlPart(string guiaPath, string xmlNewNode, XmlDocument xdocOriginal)
        {
            XmlDocument xdocReapresentacao = new XmlDocument();
            byte[] buffer = Encoding.GetEncoding("ISO-8859-1").GetBytes(xdocOriginal.InnerXml);
            try
            {
                using (WordprocessingDocument wdocReapresentacao = WordprocessingDocument.Open(guiaPath, true))
                {
                    foreach (CustomXmlPart cPartReapresentacao in wdocReapresentacao.MainDocumentPart.CustomXmlParts)
                    {
                        //Carrega a parte
                        xdocReapresentacao.Load(cPartReapresentacao.GetStream());
                        if (xdocReapresentacao.InnerXml.Contains(xmlNewNode))
                        {
                            cPartReapresentacao.GetStream().SetLength(buffer.Length);
                            cPartReapresentacao.GetStream().Write(buffer, 0, buffer.Length);
                            break;
                        }
                    }
                }
            }
            catch (Exception exx)
            { throw exx; }
        }

        /// <summary>
        /// Substitui MainDocumentPart da guia pelo XmlDocument passados como parâmetro
        /// </summary>
        /// <param name="guiaPath">Path da guia a ser manipulada</param>
        /// <param name="xmlMainDoc">XML da nova MainDocumentPart</param>
        public static void SubstituiMainDocumentPart(string guiaPath, XmlDocument xmlMainDoc)
        {
            // O Encode do MainDocumentPart é UTF-8 (Word), diferente da mensagem TISS (ISO-8859-1)
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(xmlMainDoc.InnerXml);
            try
            {
                using (WordprocessingDocument wdoc = WordprocessingDocument.Open(guiaPath, true))
                {
                    wdoc.MainDocumentPart.GetStream().SetLength(buffer.Length);
                    wdoc.MainDocumentPart.GetStream().Write(buffer, 0, buffer.Length);
                }
            }
            catch (Exception exx)
            { throw exx; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="objetoTiss">Objeto que deve ser serializado para o Stream de retorno.</param>
        /// <param name="rootObjeto">Raiz do XML (ex. XMLUtils.ElementRootWithANSnamespace("guiaConsulta")). Pode ser nulo.</param>
        /// <returns></returns>
        public static Stream Serializa(object objetoTiss, XmlRootAttribute rootObjeto)
        {
            // Namespace
            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add("ans", "http://www.ans.gov.br/padroes/tiss/schemas");
            ns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
            ns.Add("xsd", "http://www.w3.org/2001/XMLSchema");

            XmlSerializer serializer;
            if (rootObjeto != null)
                serializer = new XmlSerializer(objetoTiss.GetType(), rootObjeto);
            else
                serializer = new XmlSerializer(objetoTiss.GetType());

            Stream serializaStream = new MemoryStream();

            try
            {
                // Serializa o objeto
                XmlTextWriter writer = new XmlTextWriter(serializaStream, Encoding.GetEncoding("ISO-8859-1"));
                serializer.Serialize(writer, objetoTiss, ns);
                serializaStream.Position = 0;

                return serializaStream;
            }
            catch (Exception e)
            {
                throw new Exception("Erro ao Serializar objeto: " + e.Message);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="objetoTiss">Objeto a ser deserializado.</param>
        /// <param name="tipoObjeto">Tipo do objeto a ser deserializado.</param>
        /// <param name="rootObjeto">Raiz do XML (ex. XMLUtils.ElementRootWithANSnamespace("guiaConsulta")). Pode ser nulo.</param>
        /// <returns></returns>
        public static object Deserializa(Stream objetoTiss, Type tipoObjeto, XmlRootAttribute rootObjeto)
        {
            objetoTiss.Position = 0;
            object retorno = null;

            XmlSerializer serializer;
            if (rootObjeto != null)
                serializer = new XmlSerializer(tipoObjeto, rootObjeto);
            else
                serializer = new XmlSerializer(tipoObjeto);

            try
            {
                // Deserializa o objeto
                retorno = serializer.Deserialize(objetoTiss);
                return retorno;
            }
            catch (Exception ex)
            {
                throw new Exception("Erro ao Deserealizar objeto: " + ex.Message);
            }
        }

        public static XmlRootAttribute ElementRootWithANSnamespace(string elementName)
        {
            XmlRootAttribute xRoot = new XmlRootAttribute();
            xRoot.ElementName = elementName;
            xRoot.Namespace = "http://www.ans.gov.br/padroes/tiss/schemas";
            xRoot.IsNullable = true;
            return xRoot;
        }

        public static XmlSerializerNamespaces ANSnamespace()
        {
            XmlSerializerNamespaces nspc = new XmlSerializerNamespaces();
            nspc.Add("ans", NamespaceANS);
            return nspc;
        }
        #endregion
    }
}
